package com.jiyongjun.bms.tool.system;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <h2></h2>
 * description:
 *
 * @author yongjun.ji
 * @since 1.0 2019/5/30
 */
@Slf4j
public class IpUtils {
    private static final String REGEX = "((25[0-5]|2[0-4]\\d|1\\d{2}|0?[1-9]\\d|0?0?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|0?[1-9]\\d|0?0?\\d)";

    private static final Pattern PATTERN = Pattern.compile(REGEX);

    private static final int ONE = 1;

    private static final int TWO = 2;

    private static final int THREE = 3;

    private static final int FOUR = 4;

    private static final int ZERO = 0;

    private static final int ONE_BYTE_LENGTH = 8;

    private static final int TWO_BYTE_LENGTH = 16;

    private static final int THREE_BYTE_LENGTH = 24;
    private static final Long MAX_ADDRESS_NUM = 4294967295L;
    private static final int OX00FFFFFF = 0x00ffffff;
    private static final int OX0000FFFF = 0x0000ffff;
    private static final int OX000000FF = 0x000000ff;

    private static final Long A_LOCAL_START = 167772160L;
    private static final Long A_LOCAL_END = 184549375L;
    private static final Long B_LOCAL_START = 2886729728L;
    private static final Long B_LOCAL_END = 2887778303L;
    private static final Long C_LOCAL_START = 3232235520L;
    private static final Long C_LOCAL_END = 3232301055L;

    private static Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+");

    private IpUtils() {
    }

    /**
     * 获得网页请求IP
     * <br/> Created on 2012-12-20 下午2:41:08
     *
     * @param request
     * @return 返回客户端浏览器IP，如果无法获取争取IP则返回NULL
     * @author 李青原(liqingyuan1986 @ aliyun.com)
     * @since 1.0
     */
    public static String getClientIp(HttpServletRequest request) {
        //浏览器IP判断
        String ipArray = request.getHeader("x-forwarded-for");
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getHeader("Proxy-Client-IP");
        }
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getRemoteAddr();
        }
        if (ipArray != null) {
            String browserIp = ipArray.split(",")[0];
            //容错，如果IP错误，不走IP判断
            if (IpUtils.checkIpStr(browserIp)) {
                return browserIp;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    public static String getClientIpChain(HttpServletRequest request) {
        //浏览器IP判断
        String ipArray = request.getHeader("x-forwarded-for");
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getHeader("Proxy-Client-IP");
        }
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getRemoteAddr();
        }
        return ipArray;
    }

    /**
     * 获得网页请求IP.
     * 严格的使用方式,将不会使用request.getRemoteAddr()方法作为最后选择.
     * <br/> Created on 2013-11-25 下午3:18:25
     *
     * @param request
     * @return
     * @author 李青原(liqingyuan1986 @ aliyun.com)
     * @since 3.2.9
     */
    public static String getStrictClientIp(HttpServletRequest request) {
        //浏览器IP判断
        String ipArray = request.getHeader("x-forwarded-for");
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getHeader("Proxy-Client-IP");
        }
        if (ipArray == null || ipArray.length() == 0 || "unknown".equalsIgnoreCase(ipArray)) {
            ipArray = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipArray != null) {
            String browserIp = ipArray.split(",")[0];
            //容错，如果IP错误，不走IP判断
            if (IpUtils.checkIpStr(browserIp)) {
                return browserIp;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 根据点分式IP地址返回十进制IP数值
     * <br/> Created on 2012-12-20 下午2:40:37
     *
     * @param ip 点分式IP地址
     * @return 点分式IP地址对应的32位二进制数的十进制格式
     * @author 李青原(liqingyuan1986 @ aliyun.com)
     * @since 1.0
     */
    public static long ipToLongNum(String ip) {
        if (checkIpStr(ip)) {
            String[] ipStrArray = ip.split("\\.");
            Long[] ipLongArray = new Long[FOUR];
            for (int i = 0; i < ipStrArray.length; i++) {
                ipLongArray[i] = Long.parseLong(ipStrArray[i]);
            }
            return (ipLongArray[ZERO] << THREE_BYTE_LENGTH) + (ipLongArray[ONE] << TWO_BYTE_LENGTH) + (ipLongArray[TWO] << ONE_BYTE_LENGTH) + ipLongArray[THREE];
        } else {
            throw new IllegalArgumentException("点分式IP地址格式错误！");
        }
    }

    /**
     * 根据十进制IP数值返回点分式IP地址
     * <br/> Created on 2012-12-20 下午2:40:01
     *
     * @param addrNum 十进制IP数值
     * @return 点分式IP地址
     * @author 李青原(liqingyuan1986 @ aliyun.com)
     * @since 1.0
     */
    public static String longNumToIp(Long addrNum) {
        if (addrNum >= 0L && addrNum <= MAX_ADDRESS_NUM) {
            Long[] ipLongArray = new Long[FOUR];
            ipLongArray[ZERO] = addrNum >> THREE_BYTE_LENGTH;
            ipLongArray[ONE] = (addrNum & OX00FFFFFF) >> TWO_BYTE_LENGTH;
            ipLongArray[TWO] = (addrNum & OX0000FFFF) >> ONE_BYTE_LENGTH;
            ipLongArray[THREE] = addrNum & OX000000FF;
            return StringUtils.join(ipLongArray, '.');
        } else {
            throw new IllegalArgumentException("十进制IP数值超出IP范围！");
        }
    }

    /**
     * 判断点分式IPV4格式是否正确
     * <br/> Created on 2012-12-20 下午2:39:52
     *
     * @param ipAddress
     * @return
     * @author 李青原(liqingyuan1986 @ aliyun.com)
     * @since 1.0
     */
    public static boolean checkIpStr(String ipAddress) {
        Matcher m = PATTERN.matcher(ipAddress);
        return m.matches();
    }

    /**
     * 判断点分式IP地址是否是内网IP
     * <br/> Created on 2012-12-20 下午2:39:52
     *
     * @param ipAddress
     * @return
     * @author 李青原(liqingyuan1986 @ aliyun.com)
     * @since 3.2.9
     */
    public static boolean isLocalIp(String ipAddress) {
        if (!checkIpStr(ipAddress)) {
            return false;
        } else {
            Long ip = ipToLongNum(ipAddress);
            boolean isLocalA = (ip >= A_LOCAL_START && ip <= A_LOCAL_END);
            boolean isLocalB = (ip >= B_LOCAL_START && ip <= B_LOCAL_END);
            boolean isLocalC = (ip >= C_LOCAL_START && ip <= C_LOCAL_END);
            if (isLocalA || isLocalB || isLocalC) {
                return true;
            }
            return false;
        }
    }

    /**
     * 从url中抽取ip地址
     * http://1.1.1.1:8080/s/t/index.html
     * https://1.1.1.1:8080/s/t/index.html
     *
     * @param url
     * @return
     */
    public static String extractIp(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        String remoteServiceIp = null;

        Matcher m = p.matcher(url);
        if (m.find()) {
            remoteServiceIp = m.group();
        }
        return remoteServiceIp;
    }

    /**
     * 获得本机局域网IP，兼容linux,未取到返回NULL.<br/>
     * <note>本方法资源消耗较重,在大量反复调用时,会比较耗时.</note>
     * 获取本机ip地址
     * 此方法为重量级的方法，不要频繁调用
     * <br/> Created on 2013-11-21 下午2:36:27
     *
     * @return
     * @author 李洪波(hb.li @ zhuche.com)
     * @since 3.2
     */
    public static String getLocalIp() {
        try {
            //根据网卡取本机配置的IP
            Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
            String ip = null;
            a:
            while (netInterfaces.hasMoreElements()) {
                NetworkInterface ni = netInterfaces.nextElement();
                Enumeration<InetAddress> ips = ni.getInetAddresses();
                while (ips.hasMoreElements()) {
                    InetAddress ipObj = ips.nextElement();
                    if (ipObj.isSiteLocalAddress()) {
                        ip = ipObj.getHostAddress();
                        break a;
                    }
                }
            }
            return ip;
        } catch (Exception e) {
            log.error("", e);
            return null;
        }
    }

    /**
     * 获取本地机器名
     * 此方法为重量级的方法，不要频繁调用
     * 一般耗时在百毫秒，缓存使用
     *
     * @return
     * @throws UnknownHostException
     */
    public static String getLocalHostName() {

        String hostName = null;
        try {
            hostName = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            System.out.println("get hostname error" + e);

            log.error("get hostname error", e);
        }
        log.info("get local hostName ：" + hostName);
        return hostName;
    }
}
